<?php
namespace service\util;
use service\util\Image;
use OSS\OssClient;
use think\image\Exception;
use think\Response;
use OSS\Core\OssException;
use OSS\Core\OssUtil;

class File {

    /**
     * @var array 各类型验证规则定义(size=限制大小,ext=限制文件后缀)
     */
    const VALIDATE_TYPE = [
      'image'  => [
          'size' => 8388608, //8M
          'ext'  => 'jpg,png,gif,jpeg'
      ],
      'video'  => [
          'size' => 52428800, //50M
//          'ext'  => 'rm,rmvb,mpeg,wmv,avi,mov,swf,mp4'
          'ext'  => 'mp4,mov'
      ],
      'doc'    => [
          'size' => 5242880, //5M
          'ext'  => 'doc,docx,xls,xlsx,ppt,pptx'
      ],
      'compress' => [ // application/zip
          'size' => 52428800, //50M
          'ext'  => '7z,RAR,zip,rar,doc,docx,pdf'
      ]
    ];

    /**
     * 上传文件
     * @param \think\File|array $files
     * @param string $path
     * @thumb 是否压缩缩列图 20190708
     * @param array $validate
     * @return array 详细信息
     */
    public static function upload($files, $path, $validate = [],$module,$blur,$thumb=false) {
        $info = [];
        if (is_array($files)) {
            foreach ($files as $f) {
                $info[] = self::save($f,$path,$validate,$module,$blur,$thumb);
            }
        } else {
            $info[] = self::save($files,$path,$validate,$module,$blur,$thumb);
        }
        return $info;
    }

    /**
     * 保存文件
     * @param \think\File $file
     * @param $path
     * @param array $validate
     * @thumb 是否压缩缩列图 20190708
     * @return array
     */
    private static function save(\think\File $file, $path, $validate = [],$module,$blur,$thumb=false) {

        $info = [];
        if ($new_file = $file->validate($validate)->move($path)) {
            $info['code'] = 0;
            $info['is_success'] = true;
            $info['file_path'] = str_replace(config('file_upload_path_base'),'./',$path).str_replace('\\','/',$new_file->getSaveName());
            $info['thumb_path'] = "";
            $info['thumb_blur_path'] = "";//模糊小图地址 20190827
            //20190312 获取文件名称+当前时间
//            return $new_file->getSaveName();  //的结果 20180828/4ebb765a7f49bf09d57644ac2980949a.jpg
//            $info['file_size'] = $file->getSize();
//            $info['original_name'] = $file->getInfo('name');
//            if ($file->checkImg()) {
//                $info['is_image'] = true;
//                $info['image_width'] = \think\Image::open($file)->width();
//                $info['image_height'] = \think\Image::open($file)->height();
//            }
//            debugres($file);
            // 阿里云oss上传图片。
            $oss_path=ltrim($info['file_path'],'./');

            $base_path = config('file_upload_path_base').$oss_path;

            $is_img = true;
            if(isset($_FILES['files']['type']) && strpos( $_FILES['files']['type'],'pplication') > 0 ){
                $is_img = false;
            }
            //图片模糊图   这个功能 买设计在用，有缩略图，我就不要模糊图了。
            if ($blur && $is_img && !$thumb){
                $img = new Image();
                $arr = explode('/',$oss_path);
                $arr_last = array_pop($arr);
                $name_arr = explode('.',$arr_last); //4ebb765a7f49bf09d57644ac2980949a.jpg 分成两部分
                $name = md5($name_arr[0]);
                $ext = $name_arr[1];
                $img_name = $name.'.'.$ext;
                $blur_oss_path = $arr[0].'/'.$arr[1].'/'.$img_name;
                $blurFactor = 7;
                ini_set("memory_limit", "10000M");
//                \think\Image::open($file) 在苹果系统和linux系统找不到临时文件。不知道是什么问题。
                $image = \think\Image::open($path.$new_file->getSaveName());
                $image_width = $image->width();
                $image_height = $image->height();
                if($image_width*$image_height<250000){
                    $blurFactor = 4;
                }else if($image_width*$image_height>250000 && $image_width*$image_height<1000000){
                    $blurFactor = 5;
                }else if($image_width*$image_height>1000000 && $image_width*$image_height<25000000){
                    $blurFactor = 6;
                }else{
                    $blurFactor = 7;
                }
                //原图模糊。
                $blur_base_path = $img->gaussian_blur($base_path,null,$img_name,$blurFactor);
                $info['blur_path'] = './'.$blur_oss_path;
                self::oss_upload($blur_oss_path,$blur_base_path,$module);
            }

            //20190708 缩略图处理
            if($thumb && $is_img){
                $arr = explode('/',$oss_path);
                $arr_last = array_pop($arr);
                $img_name = 'thumb_'.$arr_last;
                $thumb_oss_path = $arr[0].'/'.$arr[1].'/'.$img_name;
                //获取保存地址
                $arr1 = explode('/',$base_path);
                $arr_last1 = array_pop($arr1);
                $name_arr = explode('.',$arr_last); //4ebb765a7f49bf09d57644ac2980949a.jpg 分成两部分
                $ext = $name_arr[1];
                $img_name1 = 'thumb_'.md5($arr_last1).".".$ext;
                $thumb_base_path = "";
                foreach($arr1 as $v){
                    $thumb_base_path .= $v."/";
                }
                $thumb_base_path .= $img_name1;
                /*
                $file_data['base_path'] = $base_path;
                $file_data['oss_path'] = $oss_path;
                $file_data['thumb_oss_path'] = $thumb_oss_path;
                $file_data['thumb_base_path'] = $thumb_base_path;
                $file_data['open_path'] = $path.$new_file->getSaveName();
                return $file_data;
               */
                $image = \think\Image::open($path.$new_file->getSaveName());
                $image->thumb(350,350)->save($thumb_base_path);
                $info['thumb_path'] = './'.$thumb_oss_path;

            }

            //20190827 模糊缩略图处理 $thumb_oss_path 本地缩略图地址
            if ($blur && $thumb && $is_img){
                $img = new Image();
                $arr = explode('/',$oss_path);
                $arr_last = array_pop($arr);
                $name_arr = explode('.',$arr_last); //4ebb765a7f49bf09d57644ac2980949a.jpg 分成两部分
                $name = md5($name_arr[0]);
                $ext = $name_arr[1];
                $img_name = 'thumb_blur_'.$name.'.'.$ext;
                $blur_oss_path = $arr[0].'/'.$arr[1].'/'.$img_name;
                $blurFactor = 1;
                ini_set("memory_limit", "10000M");
//                \think\Image::open($file) 在苹果系统和linux系统找不到临时文件。不知道是什么问题。
                $image = \think\Image::open($path.$new_file->getSaveName());
                $image_width = $image->width();
                $image_height = $image->height();
//                if($image_width*$image_height<250000){
//                    $blurFactor = 1;
//                }else if($image_width*$image_height>250000 && $image_width*$image_height<1000000){
//                    $blurFactor = 1;
//                }else if($image_width*$image_height>1000000 && $image_width*$image_height<25000000){
//                    $blurFactor = 3;
//                }else{
//                    $blurFactor = 3;
//                }
                //缩略图模糊。
                $blur_thumb_path = $img->gaussian_blur($thumb_base_path,null,$img_name,$blurFactor);
                $info['thumb_blur_path'] = './'.$blur_oss_path;
                self::oss_upload($blur_oss_path,$blur_thumb_path,$module); //上传缩略模糊图
                self::oss_upload($thumb_oss_path,$thumb_base_path,$module);//上传缩略图
            }


            self::oss_upload($oss_path,$base_path,$module); //原图

        } else {
            $info['code'] = 1;
            $info['is_success'] = false;
            $info['original_name'] = $file->getInfo('name');
            $info['error_msg'] = $file->getError();
        }

        return $info;

    }

    /**
     * 实例化阿里云oos
     * @return object 实例化得到的对象
     */
    public static function new_oss($module, $ok = false){
//        Vendor('OSS.autoload');
        if ($module == 'DesignBuy'){
            $config=config('ali_oss.aliyun_oss_design'); //这个是可以的。
        }else{
            $config=config('ali_oss.aliyun_oss'); //这个是可以的。
        }
        return new OssClient($config['KeyId'],$config['KeySecret'],$config['Endpoint'],true);
    }

    /**
     * 上传文件到oss并删除本地文件
     * @param $oss_path string 文件路径
     * @param $base_path
     * @param $module
     * @return bool
     * @throws OssException
     */
    public static function oss_upload($oss_path,$base_path,$module){
//        Vendor('OSS.autoload');
        if ($module == 'DesignBuy'){
            $config=config('ali_oss.aliyun_oss_design'); //这个是可以的。
            // 配置项  清除缓存。
//            $options = array(
//                \OSS\OssClient::OSS_HEADERS => array(
//                    'cache-control' => 'no-cache'
//                ));
            $bucket=$config['Bucket'];
            // 实例化oss类
            $oss= self::new_oss($module);
            // 上传到oss
            $oss->uploadFile($bucket,$oss_path,$base_path);


        }else{
            $config=config('ali_oss.aliyun_oss'); //这个是可以的。
            $bucket=$config['Bucket'];
            // 实例化oss类
            $oss= self::new_oss($module);
            // 上传到oss
            $oss->uploadFile($bucket,$oss_path,$base_path);


        }

        // 如需上传到oss后 自动删除本地的文件 则删除下面的注释
        @unlink($base_path);
        return true;
    }

    /**
     * 大文件分片上传
     * @param $oss_path oss路径
     * @param $base_path 本地文件完整路径
     * @return bool
     */
    public static function multipartUpload($base_path,$oss_path){
//        Vendor('OSS.autoload');
        $config=config('aliyun_oss');
        $bucket=$config['Bucket'];
        // 初始化uploadId
        try{
            $ossClient= self::new_oss($module='',true);
            //返回uploadId，它是分片上传事件的唯一标识，您可以根据这个ID来发起相关的操作，如取消分片上传、查询分片上传等。
            $uploadId = $ossClient->initiateMultipartUpload($bucket, $oss_path);
        } catch(OssException $e) {
            printf(__FUNCTION__ . ": initiateMultipartUpload FAILED\n");
            printf($e->getMessage() . "\n");
            return;
        }


        // 上传分片
        $partSize = 10 * 1024 * 1024; //10M
        $uploadFileSize = filesize($base_path);
        $pieces = $ossClient->generateMultiuploadParts($uploadFileSize, $partSize);
        $responseUploadPart = array();
        $uploadPosition = 0;
        $isCheckMd5 = true;
        foreach ($pieces as $i => $piece) {
            $fromPos = $uploadPosition + (integer)$piece[$ossClient::OSS_SEEK_TO];
            $toPos = (integer)$piece[$ossClient::OSS_LENGTH] + $fromPos - 1;
            $upOptions = array(
                $ossClient::OSS_FILE_UPLOAD => $base_path,
                $ossClient::OSS_PART_NUM => ($i + 1),
                $ossClient::OSS_SEEK_TO => $fromPos,
                $ossClient::OSS_LENGTH => $toPos - $fromPos + 1,
                $ossClient::OSS_CHECK_MD5 => $isCheckMd5,
            );
            // MD5校验。
            if ($isCheckMd5) {
                $contentMd5 = OssUtil::getMd5SumForFile($base_path, $fromPos, $toPos);
                $upOptions[$ossClient::OSS_CONTENT_MD5] = $contentMd5;
            }
            try {
                // 上传分片。
                $responseUploadPart[] = $ossClient->uploadPart($bucket, $oss_path, $uploadId, $upOptions);
            } catch(OssException $e) {
                printf(__FUNCTION__ . ": initiateMultipartUpload, uploadPart - part#{$i} FAILED\n");
                printf($e->getMessage() . "\n");
                return;
            }

//            printf(__FUNCTION__ . ": initiateMultipartUpload, uploadPart - part#{$i} OK\n"); //要注释掉
        }
        // $uploadParts是由每个分片的ETag和分片号（PartNumber）组成的数组。
        $uploadParts = array();
        foreach ($responseUploadPart as $i => $eTag) {
            $uploadParts[] = array(
                'PartNumber' => ($i + 1),
                'ETag' => $eTag,
            );
        }

        // 完成上传
        try {
            // 在执行该操作时，需要提供所有有效的$uploadParts。OSS收到提交的$uploadParts后，会逐一验证每个分片的有效性。当所有的数据分片验证通过后，OSS将把这些分片组合成一个完整的文件。
            $ossClient->completeMultipartUpload($bucket, $oss_path, $uploadId, $uploadParts);

        }  catch(OssException $e) {
            printf(__FUNCTION__ . ": completeMultipartUpload FAILED\n");
            printf($e->getMessage() . "\n");
            return;
        }
//        printf(__FUNCTION__ . ": completeMultipartUpload OK\n");  //要注释掉
        return true;
    }

}